/* TFM Music Maker compiled data player */
/* C version for SMD                    */
/* by Alone Coder and Shiru, 20.10.07   */

/* gcc version 12.12.07 */

#include "config.h"
#include "types.h"

#include "tfcplay.h"

#include "ym2612.h"


#define ym2612wr(reg, val, bank)    YM2612_writeReg(bank, reg, val)


static struct {
    u8 *data;
    u16 play;
    struct {
        u16 ptr;
        u16 wait;
        u16 freq;
        u16 retblk;
        u16 loopadr;
        u16 rep;
    } chn[6];
} TFCP = {
    NULL,
    FALSE,
    {}
};


void TFC_init(const u8 *music)
{
    TFCP.data=(u8*)music;
    TFC_play(FALSE);
}

void TFC_frame()
{
    u16 aa,chn,rchn,bank,key,tag,off,freq,frameptr,getptr;

    if(!TFCP.play) return;

    frameptr=0;
    getptr=FALSE;

    for(chn=0;chn<6;chn++)
    {
        if(TFCP.chn[chn].wait<0xff)
        {
            TFCP.chn[chn].wait++;
            continue;
        }

        if(chn<3)
        {
            bank=0;
            key=chn;
            rchn=chn;
        }
        else
        {
            bank=1;
            key=chn+1;
            rchn=chn-3;
        }

        if(TFCP.chn[chn].rep>0)
        {
            TFCP.chn[chn].rep--;
            if(!TFCP.chn[chn].rep) TFCP.chn[chn].ptr=TFCP.chn[chn].retblk;
        }

        while(1)
        {
            tag=TFCP.data[TFCP.chn[chn].ptr++];

            switch(tag)
            {
            case 0x7e:/*01111110 begin*/
                TFCP.chn[chn].loopadr=TFCP.chn[chn].ptr;
                continue;

            case 0x7f:/*01111111 end*/
                TFCP.chn[chn].ptr=TFCP.chn[chn].loopadr;
                continue;

            case 0xd0:/*11010000 repeat block*/
                TFCP.chn[chn].rep=TFCP.data[TFCP.chn[chn].ptr++];
                off=TFCP.data[TFCP.chn[chn].ptr++]<<8;
                off+=TFCP.data[TFCP.chn[chn].ptr++];
                TFCP.chn[chn].retblk=TFCP.chn[chn].ptr;
                TFCP.chn[chn].ptr+=(s16)off;
                continue;

            case 0xbf:/*10111111 use old frame data disp16*/
                off=TFCP.data[TFCP.chn[chn].ptr++]<<8;
                off+=TFCP.data[TFCP.chn[chn].ptr++];
                frameptr=TFCP.chn[chn].ptr+(s16)off;
                tag=TFCP.data[frameptr++];
                break;

            case 0xff:/*11111111 use old frame data disp8*/
                off=TFCP.data[TFCP.chn[chn].ptr++]-256;
                frameptr=TFCP.chn[chn].ptr+(s16)off;
                tag=TFCP.data[frameptr++];
                break;

            default:
                if(tag>=0xe0)/*skip 32..2 frames*/
                {
                    TFCP.chn[chn].wait=tag;
                    break;
                }
                if(tag>=0xc0)/*slide d+16*/
                {
                    freq=TFCP.chn[chn].freq;
                    freq=(freq&0xff00)+((freq+tag+0x30)&0xff);
                    TFCP.chn[chn].freq=freq;
                    ym2612wr(0xa4+rchn,freq>>8,bank);
                    ym2612wr(0xa0+rchn,freq&0xff,bank);
                    break;
                }
                frameptr=TFCP.chn[chn].ptr;
                getptr=TRUE;
            }

            if(frameptr)
            {
                if(tag&0xc0) ym2612wr(0x28,key,0);/*keyoff*/
                if(tag&0x01)/*freq*/
                {
                    freq=TFCP.data[frameptr++]<<8;
                    freq+=TFCP.data[frameptr++];
                    TFCP.chn[chn].freq=freq;
                    ym2612wr(0xa4+rchn,freq>>8,bank);
                    ym2612wr(0xa0+rchn,freq&0xff,bank);
                }
                for(aa=0;aa<((tag>>1)&0x1f);aa++)/*0..30 regs*/
                {
                    ym2612wr(TFCP.data[frameptr],TFCP.data[frameptr+1],bank);
                    frameptr+=2;
                }
                if(tag&0x80) ym2612wr(0x28,0xf0|key,0);/*keyon*/

                if(getptr)
                {
                    TFCP.chn[chn].ptr=frameptr;
                    getptr=FALSE;
                }
                frameptr=0;
            }

            break;
        }
    }
}


u16 TFC_isPlaying()
{
    return TFCP.play;
}

void TFC_play(u16 play)
{
    s16 aa,bb,cc,pp;

    if (TFCP.play == play) return;

    TFCP.play=play;

    if(!TFCP.play) /* mute ym2612 */
    {
        ym2612wr(0x2f,0,0); /* freq.scaler */
        ym2612wr(0x2d,0,0);
        ym2612wr(0x22,0,0); /* LFO off */
        ym2612wr(0x2b,0,0); /* DAC off */
        ym2612wr(0x27,0,0); /* CH3 normal mode */
        ym2612wr(0x27,0,1); /* CH3 normal mode */

        for(cc=0;cc<2;cc++)
        {
            for(aa=0;aa<3;aa++)
            {
                for(bb=0x30;bb<0x40;bb+=4) ym2612wr(aa+bb,0x00,cc); /* dt1/mul */
                for(bb=0x40;bb<0x50;bb+=4) ym2612wr(aa+bb,0x7f,cc); /* tl */
                for(bb=0x50;bb<0x60;bb+=4) ym2612wr(aa+bb,0x00,cc); /* rs/ar */
                for(bb=0x60;bb<0x70;bb+=4) ym2612wr(aa+bb,0x00,cc); /* am/d1r */
                for(bb=0x70;bb<0x80;bb+=4) ym2612wr(aa+bb,0x00,cc); /* d2r */
                for(bb=0x80;bb<0x90;bb+=4) ym2612wr(aa+bb,0x0f,cc); /* d1l/rr */
                for(bb=0x90;bb<0xa0;bb+=4) ym2612wr(aa+bb,0x00,cc); /* ssg-eg */
                ym2612wr(0xb0+bb,0x00,cc); /* fb/algo */
                ym2612wr(0xb4+bb,0x00,cc); /* ams/fms */
                ym2612wr(0x28,aa+(cc<<2),0); /* keyoff */
            }
        }
    }
    else
    {
        pp=10;
        for(aa=0;aa<6;aa++)
        {
            TFCP.chn[aa].ptr=TFCP.data[pp]+(TFCP.data[pp+1]<<8);
            TFCP.chn[aa].loopadr=TFCP.chn[aa].ptr;
            TFCP.chn[aa].wait=0xff;
            TFCP.chn[aa].rep=0;
            pp+=2;
        }
    }
}
